<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>this</title>
</head>

<body>
  <!-- 
this指向的几条基本规律(谁调用它,this就指向谁):
1. 在函数体中,非显示或隐式地简单调用函数时,
在严格模式下,函数内的this会被绑定到undefined上,
在非严格模式下则会被绑定到全局对象window/global上;

2. 一般使用new调用构造函数时,构造函数内部的this会被绑定到新创建的对象上;

3. 一般通过call/apply/bind方法显示调用函数时,函数体内的this会被绑定到指定参数的对象上;

4. 一般通过上下文对象调用函数时,函数体内的this会被绑定到该对象上;

5. 在箭头函数中,this的指向是由外层(函数或全局)作用域来决定的.

6.如果函数中的this是被上一级的对象所调用的,那么this指向的就是上一级的对象,否则指向全局对象.

7.在嵌套的关系调用中,this会指向最后调用它的对象

   -->

  <!-- %全局环境的this% -->
  <script>
    function f1() {
      console.log(this);
    }
    function f2() {
      'use strict'
      console.log(this);
    }
    f1(); //window
    f2(); //undefined

    console.log('--------------------我是分隔线--------------------');
    //题目1:
    const foo = {
      bar: 10,
      fn: function () {
        console.log(this);
        console.log(this.bar);
      }
    }
    let fn1 = foo.fn;
    fn1(); //全局环境, 输出window和undefined
    foo.fn(); //foo块级作用域 , 输出{bar:10,fn:f}这个对象和 10;
  </script>

  <!-- %上下文对象调用的this% -->
  <script>
    const person = {
      name: 'Lucas',
      borther: {
        name: 'Mike',
        fn: function () {
          return this.name
        }
      }
    }
    console.log(person.borther.fn());//Mike
    console.log('--------------------我是分隔线--------------------');
    //题目2
    const o1 = {
      text: '我是o1',
      fn: function () {
        return this.text
      }
    }
    const o2 = {
      text: '我是o2',
      fn: function () {
        return o1.fn();
      }
    }
    const o3 = {
      text: '我是o3',
      fn: function () {
        let fn = o1.fn;
        return fn();
      }
    }
    console.log(o1.fn()); //o1,this指向o1
    console.log(o2.fn()); //o1,这个还是调用的是o1的结果.
    console.log(o3.fn()); //undefined, o1是在全局定义的,所以是undefined.

    console.log('--------------------我是分隔线--------------------');
    //让o2.fn()输出o2
    const o22 = {
      text: '我是o2',
      fn: o1.fn
    }
    console.log(o22.fn());//我是o2,把函数fn挂载到o2上,this最终会指向o2.
  </script>

  <!-- 通过bind,call和apply改变this的指向 -->
  <!-- 
    它们都是用来改变相关函数this指向的,call和apply是直接进行相关函数的调用的,bind不会执行相关
    函数,而是返回一个新函数,这个新函数已经自动绑定了新的this指向,我们可以手动调用它;
    call和bind的参数设置是以逗号分隔开,而apply的参数设置是把所有参数存储到一个数组中来.
   -->
  <script>
    const aa = {
      name: 'Sloan',
      logName: function () {
        console.log(this.name);
      }
    }
    const bb = {
      name: 'mike'
    }
    aa.logName.call(bb);//mike, 把aa.logName绑定到bb上,让this的指向由aa变为指向bb. 
  </script>

  <!-- 构造函数和this -->
  <!-- 
    new操作符调用构造函数具体做了些什么呢?
    1.创建一个新的对象;
    2.将构造函数的this指向这个新对象;
    3.为这个新对象添加属性和方法;
    4.若构造函数没有显示返回其他对象,则返回这个新的对象.
    可以用代码表述为:
    let obj = {};
    obj.__proto__ = Foo.prototype;
    Foo.call(obj)
    如果构造函数显示返回一个值,而且返回的是一个对象(即返回复杂类型),那么this就指向这个返回的对象;
    如果返回的不是一个对象(即返回基本类型),那么this仍然指向实例.
   -->
  <script>
    function Foo() {
      this.bar = 'Vue';
    }
    let instance = new Foo();
    console.log(instance.bar);//Vue
    console.log('--------------------我是分隔线--------------------');
    //显示return的情况
    //场景一:返回对象
    function Foo2() {
      this.user = 'Sloan'
      const o = {}
      return o
    }
    const foo2 = new Foo2();
    console.log(foo2.user); //undefined
    //场景二:返回基本数据类型
    function Foo3() {
      this.user = 'Sloan Li'
      return 1
    }
    const foo3 = new Foo3();
    console.log(foo3.user);//Sloan Li

  </script>

  <!-- 
    箭头函数的this:
    在箭头函数中,this的指向是由外层(函数或全局)作用域决定的.
    由其所属函数或全局作用域决定的.
   -->
  <script>
    const foo4 = {
      fn: function () {
        setTimeout(function () {
          console.log(this);
          //this出现在setTimeout()的匿名函数中,this指向的是window对象
        })
      }
    }
    foo4.fn(); //window

    const foo5 = {
      fn: function () {
        setTimeout(() => {
          console.log(this);
          //this指向的外层的fn函数.
        })
      }
    }
    foo5.fn(); // {fn:f}
  </script>
  <!--  
    this的优先级问题:把通过call,apply,bind和new对this进行绑定的的情况称为显示绑定,
    而把根据调用关系确定this指向的情况称为隐式绑定.
   -->
  <script>
    function foo6(a) {
      console.log(this.a);
    }
    const obj1 = {
      a: 1,
      foo6: foo6
    }
    const obj2 = {
      a: 2,
      foo6: foo6
    }
    //可以看出,call和apply的显示绑定一般来说优先级更高
    obj1.foo6.call(obj2);//2
    obj2.foo6.call(obj1);//1

    //通过bind将bar函数中的this绑定为obj3对象,当执行bar(2)后,obj3.a的值为2,obj3对象为{a:2}.
    function foo7(a) {
      this.a = a;
    }
    const obj3 = {}
    let bar = foo7.bind(obj3); //将foo7绑定到obj3上并赋值给bar.
    bar(2);
    console.log(obj3.a);//2
    //把bar作为构造函数使用new绑定,则会输出3
    /* 
    bar函数本身是通过bind方法构造的函数,其内部已经将this绑定为obj3,
    当它再次作为构造函数通过new被调用时,返回的实例已经与obj3解绑=>
    new绑定修改了bind绑定中的this指向,因此new绑定的优先级比显示bind绑定更高.
    */
    let baz = new bar(3);
    console.log(baz.a);//3

    //箭头函数的this无法被修改
    var xxx = 123;
    const foo8 = () => xxx => {
      console.log(this.xxx);//123
    }
    const foo81 = {
      a: 2
    }
    const foo82 = {
      a: 3
    }
    const foo83 = foo8.call(foo81);
    console.log(foo83.call(foo82));
  </script>
</body>

</html>